package com.star.uc.infras.utils;

import com.star.common.core.context.SpringContext;
import com.star.common.core.context.ThreadContext;
import com.star.common.core.contract.constant.ContextConstants;
import com.star.common.core.contract.enums.ResultCodeEnum;
import com.star.common.kit.lang.StrPoolUtils;
import com.star.uc.contract.constant.UserConstants;
import com.star.uc.contract.constant.UserRedisConstants;
import com.star.uc.contract.enums.UserErrorEnum;
import com.star.uc.contract.enums.UserStatusEnum;
import com.star.uc.contract.enums.iplimit.IpLimitTypeEnum;
import com.star.uc.contract.exception.LimitRateException;
import com.star.uc.contract.param.UserIpConfigParam;
import com.star.uc.contract.param.UserLimitCountParam;
import com.star.uc.infras.properties.LimitProperties;
import com.star.uc.infras.repository.model.UserIpConfigBO;
import com.star.uc.infras.repository.model.UserLimitCountBO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * 限流工具类
 *
 * @author zhouhengzhe
 * @version 1.0
 * @date 2023/11/1 15:16
 */
@Component
@RequiredArgsConstructor
public class LimitFilterUtils {

    private final LimitProperties limitProperties;

    private final SlideWindowRateLimitUtils slideWindowRateLimitUtils;


    /**
     * ip限流（同一个应用同一个IP只能注册10个，防止人家用ip刷账号，虽然他有代理，但是我们就是要恶心他）
     *
     * @param value      手机号/邮箱
     * @param tenantId   应用ID
     * @param clientType 客户端类型
     * @param limitType  限制类型
     */
    public void limitIp(String value, String tenantId, String clientType, String limitType) {
        String ip = ThreadContext.get(ContextConstants.IP);
        UserIpConfigBO userIpConfigBO = UserIpConfigParam.builder()
                .ip(ip)
                .tenantId(tenantId)
                .build().one();
        //user:email:tenantId:ip:count:tenantId（前端）:email:ip（后端）
        String key = UserRedisConstants.USER_TENANT_ID_IP_COUNT + tenantId + StrPoolUtils.COLON + limitType + StrPoolUtils.COLON + ip;
        //如果IP白名单不为空，
        if (Objects.nonNull(userIpConfigBO)) {
            //限制次数为空或者限制次数为-1代表当前IP不限制注册
            if (Objects.isNull(userIpConfigBO.getIpLimitRegisterCount()) || userIpConfigBO.getIpLimitRegisterCount() == -1) {
                return;
            }
            //包含当前IP
            Integer ipCount = (Integer) SpringContext.getBean(RedisUtils.class).get(key);
            if (Objects.isNull(ipCount)) {
                return;
            }
            if (ipCount > userIpConfigBO.getIpLimitRegisterCount()) {
                //有可能需要人机校验，有可能需要报错
                IpLimitTypeEnum ipLimitTypeEnum = IpLimitTypeEnum.getByCode(limitProperties.getIp().getType());
                //默认不处理，需要处理，请更改配置文件application-dev.yml  LimitProperties
                ipLimitTypeEnum.validate(ip, value, tenantId, clientType);
            }
            //如果IP名单为空，则判断是否超过10次
        } else {
            Integer ipCount = (Integer) SpringContext.getBean(RedisUtils.class).get(key);
            if (Objects.nonNull(ipCount) && (ipCount > UserConstants.USER_TENANT_ID_IP_DEFAULT_COUNT)) {
                //有可能需要人机校验，有可能需要报错
                IpLimitTypeEnum ipLimitTypeEnum = IpLimitTypeEnum.getByCode(limitProperties.getIp().getType());
                //默认不处理，需要处理，请更改配置文件application-dev.yml  LimitProperties
                ipLimitTypeEnum.validate(ip, value, tenantId, clientType);
            }
        }
    }

    /**
     * 限制发送邮件或短信/ip次数
     *
     * @param value     手机号/邮箱/ip
     * @param tenantId  应用ID
     * @param limitType 客户端类型
     */
    public void limitSendEmailOrSmsOrIpCount(String value, String tenantId, String limitType, String clientType) {
        /**
         * 获取对应的限制次数配置
         */
        UserLimitCountBO userLimitCountBO = UserLimitCountParam.builder()
                .tenantId(tenantId)
                .limitType(limitType)
                .status(UserStatusEnum.NORMAL.getCode())
                .build().one();
        //不配置代表当前系统不需要限制
        if (Objects.isNull(userLimitCountBO)) {
            return;
        }
        String minuteKey = UserRedisConstants.USER_TENANT_ID_IDENTIFIER_LIMIT + tenantId + StrPoolUtils.COLON + value + "M";
        String hourKey = UserRedisConstants.USER_TENANT_ID_IDENTIFIER_LIMIT + tenantId + StrPoolUtils.COLON + value + "H";
        String dayKey = UserRedisConstants.USER_TENANT_ID_IDENTIFIER_LIMIT + tenantId + StrPoolUtils.COLON + value + "D";
        //1分钟/1小时/1天内同一个应用同一个手机号码超过limitMinuteCount次，就报错，
        long minuteLimit = slideWindowRateLimitUtils.isLimit(minuteKey, userLimitCountBO.getLimitMinuteCount(), UserConstants.MINUTE);
        if (minuteLimit > 0L) {
            throw new LimitRateException(ResultCodeEnum.REQUEST_REPEAT);
        }
        //1分钟/1小时/1天内同一个应用同一个手机号码超过limitTimeCount次，就报错，返回10056
        long hourLimit = slideWindowRateLimitUtils.isLimit(hourKey, userLimitCountBO.getLimitTimeCount(), UserConstants.HOUR);
        if (hourLimit > 0L) {
            throw new LimitRateException(UserErrorEnum.HOUR_SEND_LIMIT);
        }
        long dayLimit = slideWindowRateLimitUtils.isLimit(dayKey, userLimitCountBO.getLimitDayCount(), UserConstants.DAY);
        if (dayLimit > 0L) {
            //有可能需要人机校验，有可能需要报错，人机验证统一返回10000
            IpLimitTypeEnum ipLimitTypeEnum = IpLimitTypeEnum.getByCode(limitProperties.getIp().getType());
            //默认不处理，需要处理，请更改配置文件application-dev.yml  LimitProperties
            ipLimitTypeEnum.validate(ThreadContext.get(ContextConstants.IP), value, tenantId, clientType);
        }
    }
}
